#ifndef TYPECHECKER_HPP
#define TYPECHECKER_HPP

#include "ast/visitor.hpp"
#include "type/type.hpp"
#include <string>
#include <map>

class BinaryOp;

class TypeScope {
private:
    TypeScope* _prev;
    std::map<std::string, Type*>* _type_def;

    Type* find_local_type_def(const char* name);

public:
    TypeScope(TypeScope* prev);
    ~TypeScope();

    TypeScope* prev() { return _prev; }

    // Type definations.
    bool save_type_def(const char* name, Type* t);
    Type* find_type_def(const char* name);

    Type* apply_func(Type* func, TypeArgs* args);
    Type* transform(Type* func);
};

class EncloseScope {
private:
    std::map<std::string, Type*>* _sym_table;
    EncloseScope* _prev;

public:
    EncloseScope(EncloseScope* prev);
    ~EncloseScope();

    EncloseScope* prev() { return _prev; }
    // @Return true if variable is defined first time.
    // @Return false if conflicts.
    bool set_var_type(const char* name, Type* t);
    Type* get_var_type(const char* name);
    // Find variable in self.
    Type* find_variable(const char* name);
    void remove(const char* name);
};

class TypeChecker : public Visitor {
private:
    Type* _type;
    bool  _status;
    EncloseScope* _scope;
    TypeScope*    _type_scope;

public:
    TypeChecker();
    ~TypeChecker();

    void check_arith(OperatorType op_tp, BinaryOp* op, const char* op_name);

    virtual void visit(LogicOrNode* n);
    virtual void visit(LogicNotNode* n);
    virtual void visit(LogicAndNode* n);

    virtual void visit(BitOrNode* n);
    virtual void visit(BitXorNode* n);
    virtual void visit(BitAndNode* n);
    virtual void visit(LeftShiftNode* n);
    virtual void visit(RightShiftNode* n);

    virtual void visit(AddNode* n);
    virtual void visit(SubNode* n);
    virtual void visit(MulNode* n);
    virtual void visit(DivNode* n);
    virtual void visit(ListNode* n);
    virtual void visit(ConstInt* n);
    virtual void visit(ConstBool* n);
    virtual void visit(ConstString* n);
    virtual void visit(ConstChar* n);
    virtual void visit(VarNode* n);
    virtual void visit(VarDefNode* n);
    virtual void visit(TypeDefNode* n);
    virtual void visit(AssignNode* n);
    virtual void visit(LambdaDef* n);
    virtual void visit(CallNode* n);
    virtual void visit(PrintNode* n);
    virtual void visit(PrintlnNode* n);
    virtual void visit(IfNode* n);
    virtual void visit(CmpNode* n);

    bool status() { return _status; }
};

#endif
